﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace MonoStrategy
{
    public class MarketBuilding : BaseBuilding, IBuildingWithWorkingArea
    {
        private readonly List<Resource> m_Transports = new List<Resource>();
        private readonly int[] m_QueryJobCounts;

        public int WorkingRadius { get; internal set; }
        public IEnumerable<Resource> Transports { get { return m_Transports.AsReadOnly(); } }

        internal MarketBuilding(BuildingManager inParent, BuildingConfig inConfig, Point inPosition)
            : base(inParent, inConfig, inPosition)
        {
            int queryCount = 0;

            WorkingRadius = 5;
            WorkingArea = inPosition;

            foreach (var stack in inConfig.ResourceStacks)
            {
                if (stack.Direction != ResStackType.Query)
                    continue;

                if (stack.Type != Resource.Max)
                    throw new ArgumentException("A market building shall only contain queries of \"Resource.Max\" type.");

                queryCount++;
            }

            if (queryCount == 0)
                throw new ArgumentException("A market shall contain at least one resource query.");

            m_QueryJobCounts = new int[queryCount];
        }

        internal void AddResourceTransport(Resource inResource)
        {
            while (m_Transports.Count >= Queries.Count)
            {
                RemoveResourceTransport(m_Transports[0]);
            }

            // find empty query
            int i = 0;

            for (; i < Queries.Count; i++)
            {
                if(Queries[i] == null)
                    break;
            }

            if(i >= Queries.Count)
                throw new ApplicationException();

            // add resource query
            var stackCfg = QueriesConfig[i];

            CyclePoint stackPos = CyclePoint.FromGrid(
                             stackCfg.Position.X + Position.X,
                             stackCfg.Position.Y + Position.Y);

            Queries[i] = Parent.ResMgr.AddResourceStack(
                this,
                stackPos,
                ResStackType.Query,
                inResource,
                stackCfg.MaxStackSize);

            m_Transports.Add(inResource);
        }

        internal void RemoveResourceTransport(Resource inResource)
        {
            if (!m_Transports.Remove(inResource))
                return;

            // find resource query of given type
            GenericResourceStack query = null;
            int i = 0;

            for (; i < Queries.Count; i++)
            {
                if ((Queries[i] != null) && (Queries[i].Resource == inResource))
                {
                    query = Queries[i];

                    break;
                }
            }

            if (query == null)
                throw new ApplicationException();

            // remove resource query and drop resources around market
            Queries[i] = null;

            Parent.ResMgr.DropResource(Position.ToPoint(), query.Resource, query.Available);
            Parent.ResMgr.RemoveResource(query);
        }

        internal override bool Update()
        {
            
            bool hasTransport = false;

            // schedule one settler for each queue per update, at maximum...
            for (int fori = 0; fori < Queries.Count; fori++)
            {
                int i = fori; // localization for lambda expression
                var query = Queries[i];

                if ((query == null) || ((query.Available - m_QueryJobCounts[i]) <= 0))
                    continue;

                // find settler around query
                var settler = Parent.MovMgr.FindFreeMovableAround(query.Position.ToPoint(), MovableType.Settler);

                if (settler == null)
                    break;

                // carry resource...
                JobMarketCarrying job = new JobMarketCarrying(this, settler, query, WorkingArea);

                m_QueryJobCounts[i]++;

                settler.Job = job;
                job.OnCompleted += (unused, succeeded) => { m_QueryJobCounts[i]--; };

            }

            return hasTransport;
        }
    }
}
